package com.andong.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.andong.domain.pojo.Result;
import com.andong.domain.vo.AdminAuthVO;
import com.andong.mapper.LoginMapper;
import com.andong.service.AdminAuthService;
import com.andong.service.AdminService;
import com.andong.service.LoginService;
import com.andong.utils.BaseContext;
import com.andong.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

import static com.andong.utils.SystemConstant.*;

@Slf4j
@Component
@SuppressWarnings("unchecked")
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Resource
    private LoginService loginService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private AdminAuthService adminAuthService;
    @Override // 目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行
    public boolean preHandle(HttpServletRequest req, @NotNull HttpServletResponse resp, @NotNull Object handler) throws Exception {
        String url = req.getRequestURL().toString();
        log.info("请求的url: {}", url);

        // 2.获取请求头中的令牌（token）。
        String jwt = req.getHeader(AD_JWT);
        System.out.println(jwt);

        // 3.判断令牌是否存在，如果不存在，返回错误结果（未登录）。
        if (!StringUtils.hasLength(jwt)) {
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error(NOT_LOGIN_MESSAGE);
            // 手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return false;
        }

        // 4.解析token，如果解析失败，返回错误结果（未登录）。
        Claims claims;
        try {
            claims = JwtUtils.parseJWT(jwt);
        } catch (Exception e) {// jwt解析失败
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error(NOT_LOGIN_MESSAGE);
            // 手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return false;
        }

        // 5.
        Long id = (Long) claims.get(JWT_KEY_ID);
        String identity = (String) claims.get(JWT_KEY_IDENTITY);
        Integer status = null;

        if (!SUPER_ADMIN.equals(identity)) {
            // 获取JWT令牌中的账号状态
            status = (Integer) claims.get(JWT_KEY_STATUS);
            // 获取当前账号的真实状态
            Integer realStatus = (Integer) redisTemplate.opsForValue().get(ACCOUNT_STATUS + identity + id);
            if (Objects.isNull(realStatus)) {
                // 为空则前往数据库查询
                realStatus = loginService.selectStatusById(id, identity); // selectStatusById已经有添加缓存的逻辑
            }
            System.out.println("===========" + realStatus);
            // 判断状态是否一致
            if (!status.equals(realStatus)) {
                log.info("账号状态发生更改，返回错误信息");
                Result error = Result.error(STATUS_CHANGE);
                // 手动转换 对象--json --------> 阿里巴巴fastJSON
                String statusChange = JSONObject.toJSONString(error);
                resp.getWriter().write(statusChange);
                return false;
            }
        }
        // 如果当前身份是管理员 则校验权限信息是否发生改变
        if (ADMIN.equals(identity)) {
            // 获取当前管理员的携带的权限信息
            List<String> auths = (List<String>) claims.get(JWT_KEY_AUTH);
            // 从redis缓存获取该管理员的权限信息
            List<String> relAuths = (List<String>) redisTemplate.opsForValue().get(ADMIN_AUTH + id);
            if (Objects.isNull(relAuths)) {
                log.info("redis缓存中不存在！");
                relAuths = new ArrayList<>();
                // 前往数据库查询
                List<AdminAuthVO> adminAuthVOList = adminAuthService.getAuthById(id);// getAuth方法已经有添加缓存的逻辑
                for (AdminAuthVO adminAuthVO : adminAuthVOList) {
                    relAuths.add(adminAuthVO.getName());
                }
            }
            // 将两者作对比
            // 先排序，因为顺序可不同
            auths.sort(null);
            relAuths.sort(null);
            if (!auths.equals(relAuths)) {
                System.out.println(auths);
                System.out.println(relAuths);
                log.info("权限发生更改，返回错误信息");
                Result error = Result.error(AUTH_CHANGE);
                // 手动转换 对象--json --------> 阿里巴巴fastJSON
                String authChange = JSONObject.toJSONString(error);
                resp.getWriter().write(authChange);
                return false;
            }
        }

        System.out.println(claims.get(JWT_KEY_ID));
        String newJwt = JwtUtils.generateJwt(claims); // 生成新的JWT令牌，设置新的有效期
        // 将新的JWT令牌设置到响应头中
        resp.setHeader(AD_JWT, newJwt);
        // 在当前线程中保存id
        List<Object> list = new ArrayList<>();
        list.add(id);
        list.add(identity);
        BaseContext.setCurrentIdAndIdentity(list);
        System.out.println("==============");
        System.out.println("当前id：" + list.get(0));
        System.out.println("当前身份：" + list.get(1));
        System.out.println("================");
        // 6.放行。
        log.info("令牌合法, 放行");
        return true;
    }

    @Override // 目标资源方法运行后运行
    public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) {
        log.info("postHandle ...");
    }

    @Override // 视图渲染完毕后运行, 最后运行
    public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
        log.info("afterCompletion...");
    }
}
